﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

namespace DDD {
    /// <summary>
    /// <see cref="IndexBuffer"/>クラスに拡張メソッドを追加するクラス
    /// </summary>
    public static class IndexBufferExtensions {
        /// <summary>
        /// コンパイル
        /// </summary>
        /// <param name="ibuf"><see cref="IndexBuffer"/>オブジェクト</param>
        public static void Compile (this IndexBuffer ibuf) {
            var cache = Graphics3DExtensions.GetGLCache ();
            var id = cache[ibuf];
            if (id > 0) {
                if (ibuf.IsDirty) {
                    SendData (ibuf);
                }
                return;
            }

            GL.GenBuffers (1, out id);
            if (id <= 0) {
                throw new InvalidOperationException ("glGenBuffers was failed");
            }
            GL.BindBuffer (BufferTarget.ElementArrayBuffer, id);

            SendData (ibuf);

            GL.BindBuffer (BufferTarget.ElementArrayBuffer, 0);
            
            cache.Add (ibuf, id);
        }

        /// <summary>
        /// データをGPUに転送します
        /// </summary>
        /// <param name="ibuf">インデックスバッファー</param>
        private static void SendData (IndexBuffer ibuf) {

            var numIndices = ibuf.IndexCount;
            var numPrimitvies = ibuf.PrimitiveCount;
            var type = ibuf.IndexType;
            ErrorCode err;

            if (type == IndexType.Int) {
                var size = (IntPtr)(sizeof (int) * numIndices);
                var data = new int[numIndices];
                ((IndexBuffer<int>)ibuf).Get (0, numPrimitvies, data);
                GL.BufferData (BufferTarget.ElementArrayBuffer, size, data, BufferUsageHint.StaticDraw);
            }
            if (type == IndexType.UInt) {
                var size = (IntPtr)(sizeof (uint) * numIndices);
                var data = new uint[numIndices];
                ((IndexBuffer<uint>)ibuf).Get (0, numPrimitvies, data);
                GL.BufferData (BufferTarget.ElementArrayBuffer, size, data, BufferUsageHint.StaticDraw);
            }
            if (type == IndexType.Short) {
                var size = (IntPtr)(sizeof (short) * numIndices);
                var data = new short[numIndices];
                ((IndexBuffer<short>)ibuf).Get (0, numPrimitvies, data);
                GL.BufferData (BufferTarget.ElementArrayBuffer, size, data, BufferUsageHint.StaticDraw);
            }
            if (type == IndexType.UShort) {
                var size = (IntPtr)(sizeof (ushort) * numIndices);
                var data = new ushort[numIndices];
                ((IndexBuffer<ushort>)ibuf).Get (0, numPrimitvies, data);
                GL.BufferData (BufferTarget.ElementArrayBuffer, size, data, BufferUsageHint.StaticDraw);
            }
            if (type == IndexType.Byte) {
                var size = (IntPtr)(sizeof (byte) * numIndices);
                var data = new byte[numIndices];
                ((IndexBuffer<byte>)ibuf).Get (0, numPrimitvies, data);
                GL.BufferData (BufferTarget.ElementArrayBuffer, size, data, BufferUsageHint.StaticDraw);
            }
            if ((err = GL.GetError ()) > 0) {
                throw new InvalidOperationException ("glBufferData was failed, err=" + err);
            }

            GL.BindBuffer (BufferTarget.ElementArrayBuffer, 0);

            ibuf.SetDirtyFlag (false);
        }

        /// <summary>
        /// レンダリング
        /// </summary>
        /// <param name="ibuf"></param>
        public static void Render (this IndexBuffer ibuf) {
            var cache = Graphics3DExtensions.GetGLCache ();
            var id = cache[ibuf];
            if (id == 0) {
                throw new InvalidOperationException ("OpenGL id was not found");
            }

            GL.BindBuffer (BufferTarget.ElementArrayBuffer, id);
        }
    }
}
